package redis

import (
	"log"
	"time"

	"github.com/go-redis/redis"
)

type (
	RedisConf struct {
		Host     string
		Type     string `json:",default=node,options=node|cluster"`
		Pass     string `json:",optional"`
		Tls      bool   `json:",default=false,options=true|false"`
		DB       int    `json:",default=0"`
		PoolSize int    `json:",default=10"`
	}
)

// 根据redis配置初始化一个客户端
func MustSetup(c RedisConf) *redis.Client {
	redisDb := redis.NewClient(&redis.Options{
		Addr:     c.Host, // redis地址
		Password: c.Pass, // redis密码，没有则留空
		DB:       c.DB,   // 默认数据库，默认是0
		PoolSize: c.PoolSize,
	})

	//通过 *redis.Client.Ping() 来检查是否成功连接到了redis服务器
	_, err := redisDb.Ping().Result()
	if err != nil {
		log.Fatalf("redis connect error %+v \n", err)
	}

	return redisDb
}

func Lock(redisDb *redis.Client, key string, value string, timeout time.Duration) (ok bool, err error) {
	return redisDb.SetNX(key, value, timeout).Result()
}

func Unlock(redisDb *redis.Client, key string, value string) (bool, error) {
	script := "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return -1 end"
	reply, err := redisDb.Eval(script, []string{key}, value).Result()
	if err != nil {
		return false, err
	}
	if num, ok := reply.(int64); ok {
		if num > 0 {
			return true, nil
		}
	}
	return false, nil
}

func NextOneFromDelayed(redisDb *redis.Client, key string, min, max string) (result string, score int64, err error) {
	var (
		itemVal   string
		itemScore int64
		reply     interface{}
	)

	watchFunc := func(tx *redis.Tx) error {
		ZSliceCmd := tx.ZRangeByScoreWithScores(key, redis.ZRangeBy{
			Min: min, Max: max, Offset: 0, Count: 1,
		})
		z, err := ZSliceCmd.Result()
		if err != nil {
			return err
		}
		if len(z) != 1 {
			return redis.Nil
		}
		result := z[0]
		itemVal = result.Member.(string)
		itemScore = int64(result.Score)

		return nil

	}
	if err = redisDb.Watch(watchFunc, key); err != nil {
		return
	}
	txpipe := redisDb.TxPipeline()
	txpipe.ZRem(key, itemVal)
	reply, err = txpipe.Exec()
	if err != nil {
		return
	}
	if reply != nil {
		result = itemVal
		score = itemScore
	}
	return
}
